package com.market.dao.jpa;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.List;

import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.primefaces.model.UploadedFile;

import com.market.dao.GenericDao;
import com.market.domain.BaseObject;

/**
 * This class serves as the Base class for all other DAOs - namely to hold
 * common CRUD methods that they might all use. You should only need to extend
 * this class when your require custom CRUD logic.
 * 
 * @param <BO>
 *            a type variable
 * @param <PK>
 *            the primary key for that type
 */
public abstract class GenericDaoJpa<BO extends BaseObject, PK extends Serializable>
		implements GenericDao<BO, PK> {
	/**
	 * Log variable for all child classes. Uses LogFactory.getLog(getClass())
	 * from Commons Logging
	 */
	protected final Log log = LogFactory.getLog(this.getClass());
	private Class<BO> persistentClass;

	@PersistenceContext
	protected EntityManager em;

	/**
	 * Constructor that takes in a class to see which type of entity to persist.
	 * Use this constructor when subclassing.
	 * 
	 * @param persistentClass
	 *            the class type you'd like to persist
	 */
	public GenericDaoJpa(final Class<BO> persistentClass) {
		this.persistentClass = persistentClass;
	}

	public EntityManager getEntityManager() {
		return this.em;
	}

	public List<BO> findAll() {
		CriteriaBuilder cqBuilder = this.getEntityManager()
				.getCriteriaBuilder();
		CriteriaQuery<BO> cq = cqBuilder.createQuery(this.persistentClass);

		Root<BO> root = cq.from(this.persistentClass);
		cq.select(root);
		TypedQuery<BO> typeQuery = this.getEntityManager().createQuery(cq);
		List<BO> result = typeQuery.getResultList();
		return result;
	}

	public BO findById(PK id) {
		BO entity = em.find(this.persistentClass, id);
		if (entity == null) {
			log.debug(this.persistentClass + " Object with id " + id
					+ " can not be found, return null.");
			return null;
		} else {
			return entity;
		}
	}

	public boolean existsById(PK id) {
		if ((em.find(this.persistentClass, id) != null)) {
			return true;
		} else {
			log.debug(this.persistentClass + " Object with id " + id
					+ " can not be found, return false");
			return false;
		}
	}

	public BO merge(BO object) {
		em.merge(object);
		return object;
	}

	public BO persist(BO object) {
		em.persist(object);
		object.setIsNew(false);
		return object;
	}

	public void removeById(PK id) {
		BO bo = this.findById(id);
		if (bo != null) {
			em.remove(bo);
		} else {
			log.debug(this.persistentClass + " Object with id " + id
					+ " can not be found, remove failed");
		}
	}

	public BO save(BO object) {
		if (object.getIsNew() == true) {
			this.persist(object);
		} else {
			this.merge(object);
		}
		return object;
	}

	/**
	 * Get web application's root path, Use when upload picture and product
	 * icon's file path
	 * 
	 * @return
	 */

	protected String getRootPath() {
		return FacesContext.getCurrentInstance().getExternalContext()
				.getRealPath("");
	}

	/**
	 * Upload specific file from local machine to server
	 * 
	 * @param path
	 *            :the full path this file will be saved on the server
	 * @param fileName
	 *            :the filename this file will be saved on the server
	 * @param uploadeFile
	 *            : The fill in local machine, it will be uploaded to server
	 */
	protected void uploadFile(String path, String fileName,
			UploadedFile uploadeFile) {
		path = this.getRootPath() + "/" + path;
		File outputFile = new File(path);
		if (outputFile.exists() == false) {
			outputFile.mkdir();
		}
		outputFile = new File(path + fileName);
		OutputStream out;
		InputStream in;
		try {
			out = new FileOutputStream(outputFile);
			in = uploadeFile.getInputstream();
			byte[] result = new byte[1024];
			while (in.read(result) > 0) {
				out.write(result);
			}
			in.close();
			out.flush();
			out.close();
			in = null;
			out = null;
			System.gc();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
